using BepuPhysics.CollisionDetection;
using BepuUtilities;
using BepuUtilities.Collections;
using BepuUtilities.Memory;
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading;

namespace BepuPhysics
{
    /// <summary>
    /// 提供有效唤醒睡眠身体的功能。
    /// </summary>
    public class IslandAwakener
    {
        Solver solver;
        Statics statics;
        Bodies bodies;
        BroadPhase broadPhase;
        IslandSleeper sleeper;
        BufferPool pool;
        internal PairCache pairCache;

        public IslandAwakener(Bodies bodies, Statics statics, Solver solver, BroadPhase broadPhase, IslandSleeper sleeper, BufferPool pool)
        {
            this.bodies = bodies;
            this.statics = statics;
            this.solver = solver;
            this.broadPhase = broadPhase;
            this.sleeper = sleeper;
            this.pool = pool;

            this.phaseOneWorkerDelegate = PhaseOneWorker;
            this.phaseTwoWorkerDelegate = PhaseTwoWorker;
        }

        /// <summary>
        /// 如果身体在睡觉,它就会被唤醒。通过从Body遍历约束图可以找到的所有Body也将被唤醒。
        /// 如果身体已经苏醒,则此操作不会执行任何操作。
        /// </summary>
        /// <param name="bodyHandle">要唤醒的正文的句柄。</param>
        public void AwakenBody(BodyHandle bodyHandle)
        {
            bodies.ValidateExistingHandle(bodyHandle);
            AwakenSet(bodies.HandleToLocation[bodyHandle.Value].SetIndex);
        }

        /// <summary>
        /// 唤醒与约束关联的所有休眠实体。通过从约束参照实体遍历约束图形可以找到的所有实体也将被唤醒。
        /// 如果与约束关联的所有实体都已唤醒,则不执行任何操作。
        /// </summary>
        /// <param name="constraintHandle">要唤醒的约束的句柄。</param>
        public void AwakenConstraint(ConstraintHandle constraintHandle)
        {
            AwakenSet(solver.HandleToConstraint[constraintHandle.Value].SetIndex);
        }

        /// <summary>
        /// 唤醒集中的所有实体和约束。如果集合处于唤醒状态(索引为零),则不执行任何操作。
        /// </summary>
        /// <param name="setIndex">要唤醒的集合的索引。</param>
        public void AwakenSet(int setIndex)
        {
            if (setIndex > 0)
            {
                ValidateSleepingSetIndex(setIndex);
                // TODO：这里的一些相当无意义的工作-SPAN或其他方法可能会对API有所帮助。
                var list = new QuickList<int>(1, pool);
                list.AddUnsafely(setIndex);
                AwakenSets(ref list);
                list.Dispose(pool);
            }
        }

        /// <summary>
        /// 唤醒集合索引列表。
        /// </summary>
        /// <param name="setIndices">要唤醒的集合索引列表。</param>
        /// <param name="threadDispatcher">唤醒主体时使用的线程调度程序。传递NULL以在单个线程上运行。</param>
        public void AwakenSets(ref QuickList<int> setIndices, IThreadDispatcher threadDispatcher = null)
        {
            var uniqueSetIndices = new QuickList<int>(setIndices.Count, pool);
            var uniqueSet = new IndexSet(pool, bodies.Sets.Length);
            AccumulateUniqueIndices(ref setIndices, ref uniqueSet, ref uniqueSetIndices);
            uniqueSet.Dispose(pool);

            // 请注意,我们使用与多线程相同的代码路径,只是不使用多线程分派来执行作业。
            // TODO：如果总共只有5个正文,那么添加一些启发式方法来避免执行多线程分派可能是个好主意。
            // 应该不会有太多问题-线程变体应该只在进行大型批处理更改时才真正使用,所以拥有固定的固定成本并不是那么糟糕。
            int threadCount = threadDispatcher == null ? 1 : threadDispatcher.ThreadCount;
            // 请注意,直接唤醒总是重置活动状态。我怀疑这是足够普遍的,以至于没有人会想要另一种选择,
            // 即使在可能的情况下,为了更快地重新休眠,窄阶段确实避免重置活动状态。
            var (phaseOneJobCount, phaseTwoJobCount) = PrepareJobs(ref uniqueSetIndices, true, threadCount);

            if (threadCount > 1)
            {
                this.jobIndex = -1;
                this.jobCount = phaseOneJobCount;
                threadDispatcher.DispatchWorkers(phaseOneWorkerDelegate);
            }
            else
            {
                for (int i = 0; i < phaseOneJobCount; ++i)
                {
                    ExecutePhaseOneJob(i);
                }
            }

            if (threadCount > 1)
            {
                this.jobIndex = -1;
                this.jobCount = phaseTwoJobCount;
                threadDispatcher.DispatchWorkers(phaseTwoWorkerDelegate);
            }
            else
            {
                for (int i = 0; i < phaseTwoJobCount; ++i)
                {
                    ExecutePhaseTwoJob(i);
                }
            }

            DisposeForCompletedAwakenings(ref uniqueSetIndices);

            uniqueSetIndices.Dispose(pool);
        }

        // 请注意,只有当外部调用单独使用孤岛唤醒程序时,才会使用辅助循环及其支持字段。
        // 引擎自己对唤醒程序的使用发生在窄阶段,在此阶段,唤醒程序作业与其他作业一起调度,以获得更大的并行性。
        int jobIndex;
        int jobCount;
        // TODO：我们再一次重复此工作模式。我们已经这样做了,什么,七次吗？把它集中起来甚至不会那么困难。我们会在某个时候谈到这一点的。
        Action<int> phaseOneWorkerDelegate;
        Action<int> phaseTwoWorkerDelegate;
        internal void PhaseOneWorker(int workerIndex)
        {
            while (true)
            {
                var index = Interlocked.Increment(ref jobIndex);
                if (index >= jobCount)
                    break;
                ExecutePhaseOneJob(index);
            }
        }
        internal void PhaseTwoWorker(int workerIndex)
        {
            while (true)
            {
                var index = Interlocked.Increment(ref jobIndex);
                if (index >= jobCount)
                    break;
                ExecutePhaseTwoJob(index);
            }
        }
        enum PhaseOneJobType
        {
            PairCache,
            MoveFallbackBatchBodies,
            UpdateBatchReferencedHandles,
            CopyBodyRegion
        }

        struct PhaseOneJob
        {
            public PhaseOneJobType Type;
            public int BatchIndex; // 仅供批引用更新作业使用。
            // 仅由正文区域副本使用。
            public int SourceSet;
            public int SourceStart;
            public int TargetStart;
            public int Count;
            // 可以把这些结合起来,但这是毫无意义的。
        }
        enum PhaseTwoJobType
        {
            BroadPhase,
            CopyConstraintRegion,
        }
        struct PhaseTwoJob
        {
            public PhaseTwoJobType Type;
            public int SourceStart;
            public int TargetStart;
            public int Count;
            public int SourceSet;
            public int TypeId;
            public int Batch;
            public int SourceTypeBatch;
            public int TargetTypeBatch;
        }

        bool resetActivityStates;
        QuickList<int> uniqueSetIndices;
        QuickList<PhaseOneJob> phaseOneJobs;
        QuickList<PhaseTwoJob> phaseTwoJobs;
        internal unsafe void ExecutePhaseOneJob(int index)
        {
            ref var job = ref phaseOneJobs[index];
            switch (job.Type)
            {
                case PhaseOneJobType.PairCache:
                    {
                        // 对高速缓存的更新是本地顺序的,因为它修改了全局重叠映射,该全局重叠映射目前是哈希表。
                        // 其他每个类型的缓存可以从该作业中分离出来,并以内部多线程的方式进行处理,但这会增加可能不必要的复杂性。
                        // 我们会假设其他工作可以平衡一切,直到证明并非如此。
                        for (int i = 0; i < uniqueSetIndices.Count; ++i)
                        {
                            pairCache.AwakenSet(uniqueSetIndices[i]);
                        }
                    }
                    break;
                case PhaseOneJobType.UpdateBatchReferencedHandles:
                    {
                        // 请注意,窄阶段将与读取现有批处理引用句柄的另一个工作器一起调度此作业。
                        // 有时会有一些高速缓存线共享。这对性能来说不是很好,但应该不会导致错误：
                        // 1)窄阶段的推测性批搜索是只读的,因此没有竞争写入。
                        // 2)觉醒需要创建一个涉及睡眠主体的新约束。
                        // 然后,推测性搜索将尝试查找该约束的批次,可能会错误地结束
                        // 查找被此唤醒过程阻塞的批处理槽。
                        // 但投机过程是"投机性的";只要它不是以一种让它选择更高批次指数的方式出错,那么它错了也没关系。

                        // 请注意,这在不同的批次上是并行的,无论它们来自哪个源集。
                        Debug.Assert(job.BatchIndex < solver.FallbackBatchThreshold, "The fallback batch doesn't have any referenced handles to update!");
                        ref var targetBatchReferencedHandles = ref solver.batchReferencedHandles[job.BatchIndex];
                        for (int i = 0; i < uniqueSetIndices.Count; ++i)
                        {
                            var setIndex = uniqueSetIndices[i];
                            ref var sourceSet = ref solver.Sets[setIndex];
                            if (sourceSet.Batches.Count > job.BatchIndex)
                            {
                                ref var batch = ref sourceSet.Batches[job.BatchIndex];
                                for (int typeBatchIndex = 0; typeBatchIndex < batch.TypeBatches.Count; ++typeBatchIndex)
                                {
                                    ref var typeBatch = ref batch.TypeBatches[typeBatchIndex];
                                    solver.TypeProcessors[typeBatch.TypeId].AddWakingBodyHandlesToBatchReferences(ref typeBatch, ref targetBatchReferencedHandles);
                                }
                            }
                        }
                    }
                    break;
                case PhaseOneJobType.MoveFallbackBatchBodies:
                    {
                        for (int i = 0; i < uniqueSetIndices.Count; ++i)
                        {
                            Debug.Assert(uniqueSetIndices[i] > 0);
                            ref var source = ref solver.Sets[uniqueSetIndices[i]].Fallback;
                            ref var target = ref solver.ActiveSet.Fallback;
                            if (source.bodyConstraintReferences.Count > 0)
                            {
                                for (int j = 0; j < source.bodyConstraintReferences.Count; ++j)
                                {
                                    // 非活动集是指主体控制柄。活动集是指身体指数。完成过渡。
                                    // HandleToLocation在作业设置期间更新,因此我们可以使用它。
                                    ref var bodyLocation = ref bodies.HandleToLocation[source.bodyConstraintReferences.Keys[j]];
                                    Debug.Assert(bodyLocation.SetIndex == 0, "Any batch moved into the active set should be dealing with bodies which have already been moved into the active set.");
                                    var added = target.bodyConstraintReferences.AddUnsafelyRef(ref bodyLocation.Index, source.bodyConstraintReferences.Values[j]);
                                    Debug.Assert(added, "Any body moving from an inactive set to the active set should not already be present in the active set's fallback batch.");
                                }
                                // 我们已经重复使用了这些清单。将计数设置为零,这样它们就不会在以后被处理。
                                source.bodyConstraintReferences.Count = 0;
                            }
                        }
                    }
                    break;
                case PhaseOneJobType.CopyBodyRegion:
                    {
                        // 因为我们已经在工作准备期间预先分配了所有东西,所以我们所要做的就是从休眠集位置复制。
                        // 请注意,此处不更新与Body关联的宽阶段索引。这是在阶段2处理的;这只是将数据的睡觉放在适当的位置。
                        ref var sourceSet = ref bodies.Sets[job.SourceSet];
                        ref var targetSet = ref bodies.ActiveSet;
                        sourceSet.Collidables.CopyTo(job.SourceStart, targetSet.Collidables, job.TargetStart, job.Count);
                        sourceSet.Constraints.CopyTo(job.SourceStart, targetSet.Constraints, job.TargetStart, job.Count);
                        // 世界惯性也必须更新。它们存储在集合之外。
                        // 请注意,我们对局部惯性和姿态使用手动循环副本,因为我们无论如何都是在世界惯性计算期间访问它们的。
                        // 这可能会稍微恶化复制代码生成,但这意味着我们只需扫描内存一次。
                        // (实际上,这两个选项都很快-这些区域不会填满L1。)
                        for (int i = 0; i < job.Count; ++i)
                        {
                            var sourceIndex = job.SourceStart + i;
                            var targetIndex = job.TargetStart + i;
                            ref var targetWorldInertia = ref bodies.Inertias[targetIndex];
                            ref var sourceLocalInertia = ref sourceSet.LocalInertias[sourceIndex];
                            ref var targetLocalInertia = ref targetSet.LocalInertias[targetIndex];
                            ref var sourcePose = ref sourceSet.Poses[sourceIndex];
                            ref var targetPose = ref targetSet.Poses[targetIndex];
                            targetPose = sourcePose;
                            targetLocalInertia = sourceLocalInertia;
                            PoseIntegration.RotateInverseInertia(sourceLocalInertia.InverseInertiaTensor, sourcePose.Orientation, out targetWorldInertia.InverseInertiaTensor);
                            targetWorldInertia.InverseMass = sourceLocalInertia.InverseMass;
                        }
                        sourceSet.Velocities.CopyTo(job.SourceStart, targetSet.Velocities, job.TargetStart, job.Count);
                        sourceSet.Activity.CopyTo(job.SourceStart, targetSet.Activity, job.TargetStart, job.Count);
                        if (resetActivityStates)
                        {
                            for (int targetIndex = job.TargetStart + job.Count - 1; targetIndex >= job.TargetStart; --targetIndex)
                            {
                                ref var targetActivity = ref targetSet.Activity[targetIndex];
                                targetActivity.TimestepsUnderThresholdCount = 0;
                                targetActivity.SleepCandidate = false;
                            }
                        }
                        sourceSet.IndexToHandle.CopyTo(job.SourceStart, targetSet.IndexToHandle, job.TargetStart, job.Count);
                    }
                    break;
            }
        }


        internal unsafe void ExecutePhaseTwoJob(int index)
        {
            ref var job = ref phaseTwoJobs[index];
            switch (job.Type)
            {
                case PhaseTwoJobType.BroadPhase:
                    {
                        // 请注意,主要阶段Add/Remove依赖于正文副本;这就是它处于第二阶段的原因。
                        // 还应注意,宽相位更新不能被分成两个并行作业,因为移除更新了移动的叶子的宽相位索引,
                        // 其在上下文中可能属于尚未移除的唤醒的可碰撞物体。
                        // 如果对索引进行排序,则可以避免这种情况,并且可以将其拆分为两个作业。只有在业绩数据表明这一点的情况下才会有麻烦。
                        ref var activeSet = ref bodies.ActiveSet;
                        for (int i = 0; i < uniqueSetIndices.Count; ++i)
                        {
                            ref var sleepingBodySet = ref bodies.Sets[uniqueSetIndices[i]];
                            for (int j = 0; j < sleepingBodySet.Count; ++j)
                            {
                                // 请注意,我们必须获取可碰撞的活动版本,因为活动版本可能会被移除修改。
                                ref var bodyLocation = ref bodies.HandleToLocation[sleepingBodySet.IndexToHandle[j].Value];
                                Debug.Assert(bodyLocation.SetIndex == 0);
                                // 宽相位索引值当前是静电索引,它或者是从休眠集复制的,或者是通过下面的删除进行修改的。
                                // 我们会更新的,请参考一下。
                                ref var broadPhaseIndex = ref activeSet.Collidables[bodyLocation.Index].BroadPhaseIndex;
                                if (broadPhaseIndex >= 0)
                                {
                                    broadPhase.GetStaticBoundsPointers(broadPhaseIndex, out var minPointer, out var maxPointer);
                                    BoundingBox bounds;
                                    bounds.Min = *minPointer;
                                    bounds.Max = *maxPointer;
                                    var staticBroadPhaseIndexToRemove = broadPhaseIndex;
                                    broadPhaseIndex = broadPhase.AddActive(broadPhase.staticLeaves[broadPhaseIndex], ref bounds);

                                    if (broadPhase.RemoveStaticAt(staticBroadPhaseIndexToRemove, out var movedLeaf))
                                    {
                                        if (movedLeaf.Mobility == Collidables.CollidableMobility.Static)
                                        {
                                            statics.Collidables[statics.HandleToIndex[movedLeaf.StaticHandle.Value]].BroadPhaseIndex = staticBroadPhaseIndexToRemove;
                                        }
                                        else
                                        {
                                            // 注意,移动的叶子不能引用我们已经移动到活动集中的可碰撞对象之一,因为所有这样的可碰撞对象
                                            // 已经被移走了。
                                            bodies.UpdateCollidableBroadPhaseIndex(movedLeaf.BodyHandle, staticBroadPhaseIndexToRemove);
                                        }
                                    }
                                }
                            }
                        }
                    }
                    break;
                case PhaseTwoJobType.CopyConstraintRegion:
                    {
                        // 约束比实体稍微复杂一些,原因有几个：
                        // 1)约束以AOSOA格式存储。我们不能在没有准备的情况下简单地复制捆绑对齐,因为这可能会留下空白。
                        // 为了避免这个问题,我们改用源批次的最后一个捆绑包在目标复制区域之前填充任何不完整的捆绑包。
                        // 调度程序尝试最大限度地增加纯束副本的作业数量。
                        // 2)休眠约束将其Body引用存储为Body*Handles*而不是Body索引。
                        // 将类型批次拉回活动集中需要将这些正文句柄转换为正文索引。
                        // 3)从Body句柄到Body索引的转换要求Body已经具有活动集标识,这就是为什么约束要等到第二阶段。
                        ref var sourceTypeBatch = ref solver.Sets[job.SourceSet].Batches[job.Batch].TypeBatches[job.SourceTypeBatch];
                        ref var targetTypeBatch = ref solver.ActiveSet.Batches[job.Batch].TypeBatches[job.TargetTypeBatch];
                        Debug.Assert(targetTypeBatch.TypeId == sourceTypeBatch.TypeId);
                        solver.TypeProcessors[job.TypeId].CopySleepingToActive(
                            job.SourceSet, job.Batch, job.SourceTypeBatch, job.Batch, job.TargetTypeBatch,
                            job.SourceStart, job.TargetStart, job.Count, bodies, solver);
                    }
                    break;
            }

        }

        internal void AccumulateUniqueIndices(ref QuickList<int> candidateSetIndices, ref IndexSet uniqueSet, ref QuickList<int> uniqueSetIndices)
        {
            for (int i = 0; i < candidateSetIndices.Count; ++i)
            {
                var candidateSetIndex = candidateSetIndices[i];
                if (!uniqueSet.Contains(candidateSetIndex))
                {
                    uniqueSet.AddUnsafely(candidateSetIndex);
                    uniqueSetIndices.AllocateUnsafely() = candidateSetIndex;
                }
            }
        }
        [Conditional("DEBUG")]
        void ValidateSleepingSetIndex(int setIndex)
        {
            Debug.Assert(setIndex >= 1 && setIndex < bodies.Sets.Length && setIndex < solver.Sets.Length && setIndex < pairCache.SleepingSets.Length);
            // 请注意,如果没有对,则不保证分配对缓存集,如果没有约束,则不保证存在求解器集。
            Debug.Assert(bodies.Sets[setIndex].Allocated);
        }
        [Conditional("DEBUG")]
        void ValidateUniqueSets(ref QuickList<int> setIndices)
        {
            var set = new IndexSet(pool, bodies.Sets.Length);
            for (int i = 0; i < setIndices.Count; ++i)
            {
                var setIndex = setIndices[i];
                ValidateSleepingSetIndex(setIndex);
                Debug.Assert(!set.Contains(setIndex));
                set.Add(setIndex, pool);
            }
            set.Dispose(pool);

        }

        // 这是在进入"泡泡企业"的领域。
        interface ITypeCount
        {
            void Add<T>(T other) where T : ITypeCount;
        }
        struct ConstraintCount : ITypeCount
        {
            public int Count;

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public void Add<T>(T other) where T : ITypeCount
            {
                Debug.Assert(typeof(T) == typeof(ConstraintCount));
                Count += Unsafe.As<T, ConstraintCount>(ref other).Count;
            }
        }
        struct PairCacheCount : ITypeCount
        {
            public int ElementSizeInBytes;
            public int ByteCount;

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public void Add<T>(T other) where T : ITypeCount
            {
                Debug.Assert(typeof(T) == typeof(PairCacheCount));
                ref var pairCacheOther = ref Unsafe.As<T, PairCacheCount>(ref other);
                Debug.Assert(ElementSizeInBytes == 0 || ElementSizeInBytes == pairCacheOther.ElementSizeInBytes);
                ElementSizeInBytes = pairCacheOther.ElementSizeInBytes;
                ByteCount += pairCacheOther.ByteCount;
            }
        }

        struct TypeAllocationSizes<T> where T : unmanaged, ITypeCount
        {
            public Buffer<T> TypeCounts;
            public int HighestOccupiedTypeIndex;
            public TypeAllocationSizes(BufferPool pool, int maximumTypeCount)
            {
                pool.Take(maximumTypeCount, out TypeCounts);
                TypeCounts.Clear(0, maximumTypeCount);
                HighestOccupiedTypeIndex = 0;
            }
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public void Add(int typeId, T typeCount)
            {
                TypeCounts[typeId].Add(typeCount);
                if (typeId > HighestOccupiedTypeIndex)
                    HighestOccupiedTypeIndex = typeId;
            }
            public void Dispose(BufferPool pool)
            {
                pool.Return(ref TypeCounts);
            }
        }


        unsafe internal (int phaseOneJobCount, int phaseTwoJobCount) PrepareJobs(ref QuickList<int> setIndices, bool resetActivityStates, int threadCount)
        {
            if (setIndices.Count == 0)
                return (0, 0);
            ValidateUniqueSets(ref setIndices);
            this.uniqueSetIndices = setIndices;
            this.resetActivityStates = resetActivityStates;

            // 我们在此职能中有三个主要工作：
            // 1)确保Body、解算器和对缓存中的活动集可以容纳新唤醒的孤岛,而无需调整大小或访问缓冲池。
            // 2)为实体和约束分配激活集中的空间。
            // (由于重叠映射是全局的,所以现在将对缓存留到本地顺序任务中处理。
            // 类型缓存可以并行更新,但我们只是没有将其拆分。如果似乎有理由这样做,您可以考虑这样做。)
            // 3)计划两个工作阶段的实际作业。

            int newBodyCount = 0;
            int highestNewBatchCount = 0;
            int highestRequiredTypeCapacity = 0;
            int additionalRequiredFallbackCapacity = 0;
            for (int i = 0; i < setIndices.Count; ++i)
            {
                var setIndex = setIndices[i];
                newBodyCount += bodies.Sets[setIndex].Count;
                var setBatchCount = solver.Sets[setIndex].Batches.Count;
                if (highestNewBatchCount < setBatchCount)
                    highestNewBatchCount = setBatchCount;
                ref var constraintSet = ref solver.Sets[setIndex];
                additionalRequiredFallbackCapacity += constraintSet.Fallback.BodyCount;
                for (int batchIndex = 0; batchIndex < constraintSet.Batches.Count; ++batchIndex)
                {
                    ref var batch = ref constraintSet.Batches[batchIndex];
                    for (int typeBatchIndex = 0; typeBatchIndex < batch.TypeBatches.Count; ++typeBatchIndex)
                    {
                        ref var typeBatch = ref batch.TypeBatches[typeBatchIndex];
                        if (highestRequiredTypeCapacity < typeBatch.TypeId)
                            highestRequiredTypeCapacity = typeBatch.TypeId;
                    }
                }

            }
            // 我们累加了上述指标,加1即可得到容量需求。
            ++highestRequiredTypeCapacity;
            pool.Take<TypeAllocationSizes<ConstraintCount>>(highestNewBatchCount, out var constraintCountPerTypePerBatch);
            for (int batchIndex = 0; batchIndex < highestNewBatchCount; ++batchIndex)
            {
                constraintCountPerTypePerBatch[batchIndex] = new TypeAllocationSizes<ConstraintCount>(pool, highestRequiredTypeCapacity);
            }
            var narrowPhaseConstraintCaches = new TypeAllocationSizes<PairCacheCount>(pool, PairCache.CollisionConstraintTypeCount);
            var narrowPhaseCollisionCaches = new TypeAllocationSizes<PairCacheCount>(pool, PairCache.CollisionTypeCount);

            void AccumulatePairCacheTypeCounts(ref Buffer<SleepingCache> sourceTypeCaches, ref TypeAllocationSizes<PairCacheCount> counts)
            {
                for (int j = 0; j < sourceTypeCaches.Length; ++j)
                {
                    ref var sourceCache = ref sourceTypeCaches[j];
                    if (sourceCache.List.Buffer.Allocated)
                        counts.Add(sourceCache.TypeId, new PairCacheCount { ByteCount = sourceCache.List.ByteCount, ElementSizeInBytes = sourceCache.List.ElementSizeInBytes });
                    else
                        break; // 遇到未分配的时隙是终止条件。而不是显式存储缓存计数,后者几乎没有什么用处。
                }
            }
            int newPairCount = 0;
            for (int i = 0; i < setIndices.Count; ++i)
            {
                var setIndex = setIndices[i];
                ref var constraintSet = ref solver.Sets[setIndex];
                for (int batchIndex = 0; batchIndex < constraintSet.Batches.Count; ++batchIndex)
                {
                    ref var constraintCountPerType = ref constraintCountPerTypePerBatch[batchIndex];
                    ref var batch = ref constraintSet.Batches[batchIndex];
                    for (int typeBatchIndex = 0; typeBatchIndex < batch.TypeBatches.Count; ++typeBatchIndex)
                    {
                        ref var typeBatch = ref batch.TypeBatches[typeBatchIndex];
                        constraintCountPerType.Add(typeBatch.TypeId, new ConstraintCount { Count = typeBatch.ConstraintCount });
                    }
                }

                ref var sourceSet = ref pairCache.SleepingSets[setIndex];
                newPairCount += sourceSet.Pairs.Count;
                AccumulatePairCacheTypeCounts(ref sourceSet.ConstraintCaches, ref narrowPhaseConstraintCaches);
                AccumulatePairCacheTypeCounts(ref sourceSet.CollisionCaches, ref narrowPhaseCollisionCaches);
            }

            // 现在我们知道要添加多少个新主体、约束批处理条目和配对缓存条目。
            // 确保所有系统的容量：
            // 身体,
            bodies.EnsureCapacity(bodies.ActiveSet.Count + newBodyCount);
            // 宽泛的阶段,(技术上高估了,并不是每个人都有可碰撞的,但绝大多数人都有并耸耸肩)
            broadPhase.EnsureCapacity(broadPhase.ActiveTree.LeafCount + newBodyCount, broadPhase.StaticTree.LeafCount);
            // 约束条件,
            solver.ActiveSet.Batches.EnsureCapacity(highestNewBatchCount, pool);
            if (additionalRequiredFallbackCapacity > 0)
                solver.ActiveSet.Fallback.EnsureCapacity(solver.ActiveSet.Fallback.BodyCount + additionalRequiredFallbackCapacity, pool);
            solver.batchReferencedHandles.EnsureCapacity(Math.Min(solver.FallbackBatchThreshold, highestNewBatchCount), pool);
            for (int batchIndex = solver.ActiveSet.Batches.Count; batchIndex < highestNewBatchCount; ++batchIndex)
            {
                solver.ActiveSet.Batches.AllocateUnsafely() = new ConstraintBatch(pool);
                // 备用批处理没有引用批处理的句柄。
                if (batchIndex < solver.FallbackBatchThreshold)
                {
                    solver.batchReferencedHandles.AllocateUnsafely() = new IndexSet(pool, bodies.HandlePool.HighestPossiblyClaimedId + 1);
                }
            }
            for (int batchIndex = 0; batchIndex < highestNewBatchCount; ++batchIndex)
            {
                ref var constraintCountPerType = ref constraintCountPerTypePerBatch[batchIndex];
                ref var batch = ref solver.ActiveSet.Batches[batchIndex];
                batch.EnsureTypeMapSize(pool, constraintCountPerType.HighestOccupiedTypeIndex);
                // 备用批处理没有引用批处理的句柄。
                if (batchIndex < solver.FallbackBatchThreshold)
                {
                    solver.batchReferencedHandles[batchIndex].EnsureCapacity(bodies.HandlePool.HighestPossiblyClaimedId + 1, pool);
                }
                for (int typeId = 0; typeId <= constraintCountPerType.HighestOccupiedTypeIndex; ++typeId)
                {
                    var countForType = constraintCountPerType.TypeCounts[typeId].Count;
                    if (countForType > 0)
                    {
                        var typeProcessor = solver.TypeProcessors[typeId];
                        ref var typeBatch = ref *batch.GetOrCreateTypeBatch(typeId, typeProcessor, countForType, pool);
                        var targetCapacity = countForType + typeBatch.ConstraintCount;
                        if (targetCapacity > typeBatch.IndexToHandle.Length)
                        {
                            typeProcessor.Resize(ref typeBatch, targetCapacity, pool);
                        }
                    }
                }
                constraintCountPerType.Dispose(pool);
            }
            pool.Return(ref constraintCountPerTypePerBatch);
            // 和窄相位对高速缓存。
            ref var targetPairCache = ref pairCache.GetCacheForAwakening();
            void EnsurePairCacheTypeCapacities(ref TypeAllocationSizes<PairCacheCount> cacheSizes, ref Buffer<UntypedList> targetCaches, BufferPool cachePool)
            {
                for (int typeIndex = 0; typeIndex <= cacheSizes.HighestOccupiedTypeIndex; ++typeIndex)
                {
                    ref var pairCacheCount = ref cacheSizes.TypeCounts[typeIndex];
                    if (pairCacheCount.ByteCount > 0)
                    {
                        ref var targetSubCache = ref targetCaches[typeIndex];
                        targetSubCache.EnsureCapacityInBytes(pairCacheCount.ElementSizeInBytes, targetSubCache.ByteCount + pairCacheCount.ByteCount, cachePool);
                    }
                }
            }
            EnsurePairCacheTypeCapacities(ref narrowPhaseConstraintCaches, ref targetPairCache.constraintCaches, targetPairCache.pool);
            EnsurePairCacheTypeCapacities(ref narrowPhaseCollisionCaches, ref targetPairCache.collisionCaches, targetPairCache.pool);
            narrowPhaseConstraintCaches.Dispose(pool);
            narrowPhaseCollisionCaches.Dispose(pool);
            pairCache.Mapping.EnsureCapacity(pairCache.Mapping.Count + newPairCount, pool);

            phaseOneJobs = new QuickList<PhaseOneJob>(Math.Max(32, highestNewBatchCount + 1), pool);
            phaseTwoJobs = new QuickList<PhaseTwoJob>(32, pool);
            // 最后,创造实际的就业机会。请注意,这涉及到在Body集合中实际分配空间,并分批输入,以供工作人员填写。
            // (成对缓存当前以本地顺序方式处理,不需要预分配。)

            phaseOneJobs.AllocateUnsafely() = new PhaseOneJob { Type = PhaseOneJobType.PairCache };
            phaseOneJobs.AllocateUnsafely() = new PhaseOneJob { Type = PhaseOneJobType.MoveFallbackBatchBodies };
            // 不要为备用批创建批引用句柄更新作业;它没有引用句柄！
            var highestSynchronizedBatchCount = Math.Min(solver.FallbackBatchThreshold, highestNewBatchCount);
            for (int batchIndex = 0; batchIndex < highestSynchronizedBatchCount; ++batchIndex)
            {
                phaseOneJobs.AllocateUnsafely() = new PhaseOneJob { Type = PhaseOneJobType.UpdateBatchReferencedHandles, BatchIndex = batchIndex };
            }
            phaseTwoJobs.AllocateUnsafely() = new PhaseTwoJob { Type = PhaseTwoJobType.BroadPhase };

            ref var activeBodySet = ref bodies.ActiveSet;
            ref var activeSolverSet = ref solver.ActiveSet;
            // TODO：单线程执行的作业大小有点傻。对于特殊情况或动态大小,非常容易解决。
            for (int i = 0; i < uniqueSetIndices.Count; ++i)
            {
                var sourceSetIndex = uniqueSetIndices[i];
                {
                    const int bodyJobSize = 64;
                    ref var sourceSet = ref bodies.Sets[sourceSetIndex];
                    var setJobCount = Math.Max(1, sourceSet.Count / bodyJobSize);
                    var baseBodiesPerJob = sourceSet.Count / setJobCount;
                    var remainder = sourceSet.Count - baseBodiesPerJob * setJobCount;
                    phaseOneJobs.EnsureCapacity(phaseOneJobs.Count + setJobCount, pool);
                    var previousSourceEnd = 0;
                    for (int jobIndex = 0; jobIndex < setJobCount; ++jobIndex)
                    {
                        ref var job = ref phaseOneJobs.AllocateUnsafely();
                        job.Type = PhaseOneJobType.CopyBodyRegion;
                        job.SourceSet = sourceSetIndex;
                        job.SourceStart = previousSourceEnd;
                        job.TargetStart = activeBodySet.Count;
                        job.Count = jobIndex >= remainder ? baseBodiesPerJob : baseBodiesPerJob + 1;
                        previousSourceEnd += job.Count;
                        activeBodySet.Count += job.Count;
                        // 我们预先执行Body句柄更新,因为它很便宜,而且它使一些事情变得更简单：
                        // 窄相位刷新在第二阶段添加了约束,我们希望唤醒程序在发生时已经修改了后备批。
                        // 因此,我们在IslandAwakener的第一阶段(发生在窄阶段刷新的第一阶段)中进行后备批修改,这依赖于
                        // 正文句柄->位置映射是最新的。
                        for (int j = 0; j < job.Count; ++j)
                        {
                            var sourceIndex = job.SourceStart + j;
                            var targetIndex = job.TargetStart + j;
                            ref var bodyLocation = ref bodies.HandleToLocation[sourceSet.IndexToHandle[sourceIndex].Value];
                            bodyLocation.SetIndex = 0;
                            bodyLocation.Index = targetIndex;
                        }
                    }
                    Debug.Assert(previousSourceEnd == sourceSet.Count);
                    Debug.Assert(activeBodySet.Count <= activeBodySet.IndexToHandle.Length);
                }
                {
                    const int constraintJobSize = 32;
                    ref var sourceSet = ref solver.Sets[sourceSetIndex];
                    for (int batchIndex = 0; batchIndex < sourceSet.Batches.Count; ++batchIndex)
                    {
                        ref var sourceBatch = ref sourceSet.Batches[batchIndex];
                        ref var targetBatch = ref activeSolverSet.Batches[batchIndex];
                        for (int sourceTypeBatchIndex = 0; sourceTypeBatchIndex < sourceBatch.TypeBatches.Count; ++sourceTypeBatchIndex)
                        {
                            ref var sourceTypeBatch = ref sourceBatch.TypeBatches[sourceTypeBatchIndex];
                            var targetTypeBatchIndex = targetBatch.TypeIndexToTypeBatchIndex[sourceTypeBatch.TypeId];
                            ref var targetTypeBatch = ref targetBatch.TypeBatches[targetTypeBatchIndex];
                            // TODO：为了避免部分捆绑,在安排开始点和结束点方面稍微聪明一点会更好。
                            var jobCount = Math.Max(1, sourceTypeBatch.ConstraintCount / constraintJobSize);
                            var baseConstraintsPerJob = sourceTypeBatch.ConstraintCount / jobCount;
                            var remainder = sourceTypeBatch.ConstraintCount - baseConstraintsPerJob * jobCount;
                            phaseTwoJobs.EnsureCapacity(phaseTwoJobs.Count + jobCount, pool);

                            var previousSourceEnd = 0;
                            for (int jobIndex = 0; jobIndex < jobCount; ++jobIndex)
                            {
                                ref var job = ref phaseTwoJobs.AllocateUnsafely();
                                job.Type = PhaseTwoJobType.CopyConstraintRegion;
                                job.TypeId = sourceTypeBatch.TypeId;
                                job.Batch = batchIndex;
                                job.SourceSet = sourceSetIndex;
                                job.SourceTypeBatch = sourceTypeBatchIndex;
                                job.TargetTypeBatch = targetTypeBatchIndex;
                                job.Count = jobIndex >= remainder ? baseConstraintsPerJob : baseConstraintsPerJob + 1;
                                job.SourceStart = previousSourceEnd;
                                job.TargetStart = targetTypeBatch.ConstraintCount;
                                previousSourceEnd += job.Count;
                                targetTypeBatch.ConstraintCount += job.Count;
                            }
                            Debug.Assert(previousSourceEnd == sourceTypeBatch.ConstraintCount);
                            Debug.Assert(targetTypeBatch.ConstraintCount <= targetTypeBatch.IndexToHandle.Length);
                        }
                    }
                }
            }
            return (phaseOneJobs.Count, phaseTwoJobs.Count);
        }


        internal void DisposeForCompletedAwakenings(ref QuickList<int> setIndices)
        {
            for (int i = 0; i < setIndices.Count; ++i)
            {
                var setIndex = setIndices[i];
                ref var bodySet = ref bodies.Sets[setIndex];
                // 请注意,约束集和配对高速缓存集都不一定存在。身体自己入睡是可能的。
                ref var constraintSet = ref solver.Sets[setIndex];
                ref var pairCacheSet = ref pairCache.SleepingSets[setIndex];
                Debug.Assert(bodySet.Allocated);
                bodySet.DisposeBuffers(pool);
                if (constraintSet.Allocated)
                {
                    constraintSet.Dispose(pool);
                }
                if (pairCacheSet.Allocated)
                    pairCacheSet.Dispose(pool);
                this.uniqueSetIndices = new QuickList<int>();
                sleeper.ReturnSetId(setIndex);

            }
            phaseOneJobs.Dispose(pool);
            phaseTwoJobs.Dispose(pool);
        }
    }
}
